/* engine.c --
 * Copyright 2009  cwxstat LLC., Elkins Park, Pennsylvania.
 * All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Authors:
 *     Mike Chirico <mchirico@gmail.com>
 *     
 */




#include "engine.h"

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>

extern int errno;
static void term_handler(int sig);
static void hup_handler(int sig);
static void load_config(void);
ssize_t WriteFile(int fd, const char *s);
static void *asyc_readInput(void *arg);
static void *asyc_write(void *arg);
/* static void *asyc_read(void *arg); */
static void process();

int
init_conn(void)
{
	int i;
	pthread_t t;

	for (i = 0; i < CTBLMAX; ++i) {
		conntbl[i].conn = NULL;
		conntbl[i].t = 0;
		conntbl[i].FLAG = 0;
	}
	conn_count = -1;

	stop = 0;
	hup = 0;
	/* Register sighandlers */
	sa.sa_flags = 0;
	sigemptyset(&sa.sa_mask);
	/* Set handler for the ones we care about */
	sa.sa_handler = term_handler;
	sigaction(SIGTERM, &sa, NULL);
	sa.sa_handler = hup_handler;
	sigaction(SIGHUP, &sa, NULL);

	Pthread_create(&t, NULL, &asyc_readInput, NULL);

	return 0;
}

/*
 * SIGTERM handler
 */

/*
   Chirico - these need to be fixed. Need to reload
   if given signal.

*/
static void
term_handler(int __attribute__((unused)) sig)
{

	stop = 1;
}

/*
 * SIGHUP handler: re-read config
 */
static void
hup_handler(int __attribute__((unused)) sig)
{
	hup = 1;
}

static void
load_config(void)
{
	hup = 0;
}

/*
Take a close look at these locks. Something could
be wrong here.

*/
static void
process()
{

	int i;
	struct S_Buf *s_b;

	// CTBLMAX 0= no connection
	// CTBLMAX 1= active connection
	// CTBLMAX 2= in write state
	for (i = 0; i < CTBLMAX; ++i) {

		Pthread_mutex_lock(&conn_mutex);
		if (conntbl[i].FLAG == 1) {
			conntbl[i].FLAG = 2;
			Pthread_mutex_unlock(&conn_mutex);

			s_b = (struct S_Buf *)malloc(sizeof(struct S_Buf));
			if (s_b == NULL) {
				syslog(LOG_ERR, "s_b is NULL");
				return;
			}
			s_b->conn = conntbl[i].conn;
			s_b->s = (char *)malloc(sizeof(bbuf));
			if (s_b->s == NULL) {
				free(s_b);
				syslog(LOG_ERR,
				       "engine: (BAD) error in process s_b-> no malloc");
				break;
			}
			s_b->i = i;
			strncpy(s_b->s, bbuf, sizeof(bbuf));
			Pthread_create(&conntbl[i].t, NULL, &asyc_write, s_b);

		}
		else {
			Pthread_mutex_unlock(&conn_mutex);

		}
	}
}

static void *
asyc_write(void *arg)
{
	int err, nwrite;
	SSL *conn;
	char *s;
	int i;

	conn = ((struct S_Buf *)arg)->conn;
	s = ((struct S_Buf *)arg)->s;
	i = ((struct S_Buf *)arg)->i;

	for (nwrite = 0; nwrite < BUF_LEN; nwrite += err) {
		err = SSL_write(conn, s + nwrite, BUF_LEN - nwrite);
		if (err <= 0)
			break;

	}
	//      syslog(LOG_ERR, " asyc_write(%d)[%ld]: %s<-", err, strlen(s), s);
	Pthread_mutex_lock(&conn_mutex);
	conntbl[i].FLAG = 1;
	Pthread_mutex_unlock(&conn_mutex);
	free(s);
	free(arg);
	Pthread_detach(pthread_self());
	return (NULL);
}

static void *
asyc_readInput(void  __attribute__((unused))  *arg)
{

	do {
		load_config();
		while (fgets(bbuf, sizeof(bbuf), stdin) && hup == 0
		       && stop == 0) {
			process();
		}
		*bbuf = '\0';

	} while (hup && stop == 0);
	syslog(LOG_ERR, "engine: asyc_readInput just received hup && stop\n");
	Pthread_detach(pthread_self());
	return (NULL);
}

int
remove_conn(int me)
{
	if (me < 0)
		return (me);

	Pthread_mutex_lock(&conn_mutex);
	conntbl[me].conn = NULL;
	conntbl[me].t = 0;
	conntbl[me].FLAG = 0;
	conn_count--;
	Pthread_mutex_unlock(&conn_mutex);

	return conn_count;

}

int
assign_conn(void *arg)
{
	SSL *ssl = (SSL *) arg;
	int me = -1;
	int i;

	Pthread_mutex_lock(&conn_mutex);
	conn_count++;
	for (i = 0; i < CTBLMAX; ++i) {
		if (conntbl[i].FLAG == 0) {
			me = i;
		}
		if (me != -1)
			break;

	}
	if (me == -1) {
		/* Need to fix this */
		syslog(LOG_ERR, "too many connections\n");
		Pthread_mutex_unlock(&conn_mutex);
		return -1;
	}
	conntbl[me].conn = ssl;
	conntbl[me].t = pthread_self();
	conntbl[me].FLAG = 1;
	Pthread_mutex_unlock(&conn_mutex);

	syslog(LOG_ERR, "assign_conn completed\n");
	return me;
}

ssize_t
WriteFile(int fd, const char *s)
{
	return write(fd, s, strlen(s));

}

/*
******************************************************************************************
******************************************************************************************
*/

/* include pr_thread_id */
long
pr_thread_id(pthread_t * ptr)
{
#if defined(sun)
	return ((ptr == NULL) ? pthread_self() : *ptr);	/* Solaris */

#elif defined(__osf__) && defined(__alpha)
	pthread_t tid;

	tid = (ptr == NULL) ? pthread_self() : *ptr;	/* Digital Unix */
	return (pthread_getsequence_np(tid));
#else
	/* 4everything else */
	return ((ptr == NULL) ? pthread_self() : *ptr);
#endif
}
